/*
 * Copyright 2018 KiWiPeach.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.java8.demo05_internal_function;

import org.junit.Test;

import java.util.*;
import java.util.function.*;

/**
 * Create Date: 2018/04/09
 * Description: java8内置函数测试（
 * 、Function、Supplier、Consumer、Optional）
 *
 * @author kiwipeach [1099501218@qq.com]
 */
public class InternalMethodTest {
    class Person {
        private String name;
        private Integer age;

        public Person() {
        }

        public Person(String name) {
            this.name = name;
        }

        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    /**
     * 测试1：测试Predicates函数（布尔类型函数）
     */
    @Test
    public void test11() {
        /*
         * 传统判断方式: 同一个判断函数多出引用，需要多出声明！例如下面的`Objects.isNull`和`source.length() > 2`
         * */
        String source = "foo";
        // 字符串长度判断
        boolean isValidLen = source.length() > 2;
        boolean isNotValidLen = !(source.length() > 2);

        // 对象为空判断
        Person p1 = new Person();
        Person p2 = new Person();
        boolean isEmptyPerson1 = Objects.isNull(p1);
        boolean isEmptyPerson2 = Objects.isNull(p2);
    }

    /**
     * 使用Predicates函数接口优化
     */

    public boolean stringJudge(String value, Predicate<String> predicate) {
        return predicate.test(value);
    }

    /**
     * 传统判断方式
     * 痛点：同一个判断函数多出引用，需要多出声明！例如下面的`Objects.isNull`和`source.length() > 2`
     */
    @Test
    public void test12() {
        String source = "foo";
        Predicate<String> validLenPredicate = p -> p.length() > 2;
        // 字符串长度判断
        boolean isValidLen1 = stringJudge(source, validLenPredicate);
        boolean isNotValidLen1 = stringJudge(source, validLenPredicate.negate());

        boolean isValidLen2 = validLenPredicate.test(source);
        boolean isNotValidLen3 = validLenPredicate.negate().test(source);

        // 对象为空判断
        Person p1 = new Person();
        Person p2 = new Person();
        Predicate<String> emptyPredicate = Objects::isNull;

        boolean isEmptyPerson1 = emptyPredicate.test(source);
        boolean isEmptyPerson2 = emptyPredicate.test(source);
    }

    /**
     * 其他使用规则
     */
    @Test
    public void test13() {
        // 基础使用
        String source = "foo";
        //Predicate(谓语部分;谓语;谓词;断言)
        Predicate<String> lenPredicate = (s) -> s.length() > 2;
        System.out.println("布尔判断?" + lenPredicate.test(source));
        Predicate<Person> nonNull = Objects::nonNull;
        System.out.println("指向Objects的Person类型非空判断?" + nonNull.test(new Person()));//true
        Predicate<String> isNull = Objects::isNull;
        System.out.println("指向Objects的Person类型为空判断?" + isNull.test(null));//true
        Predicate<String> isEmpty = String::isEmpty;
        System.out.println("指向String的String类型为空判断?" + isEmpty.test(""));//true
        Predicate<String> isNotEmpty = isEmpty.negate();
        System.out.println("指向String的String类型为空判断?" + isNotEmpty.test(""));//false

        // negate测试
        System.out.println("非操作判断?" + lenPredicate.negate().test(source));//false

        // and测试
        Predicate<String> andPredicate = lenPredicate.and(isNotEmpty);
        System.out.println("and 测试：" + andPredicate.test("123456")); //true && true
        System.out.println("and 测试：" + andPredicate.test("k")); //false && true

        // or测试
        Predicate<String> orPredicate = lenPredicate.or(isNotEmpty).or(isNull);
        System.out.println("or 测试：" + orPredicate.test("123456"));//true || false|| false
        System.out.println("or 测试：" + orPredicate.test("k"));//false || true|| false

        // isEqual测试
        Person person = new Person();
        Predicate<Person> isEqualPredicate = Predicate.isEqual(person);
        System.out.println("isEqual 测试：" + isEqualPredicate.test(person));//true
        System.out.println("isEqual 测试：" + isEqualPredicate.test(new Person()));//false
        /**
         * 布尔判断?true
         * 指向Objects的Person类型非空判断?true
         * 指向Objects的Person类型为空判断?true
         * 指向String的String类型为空判断?true
         * 指向String的String类型为空判断?false
         * 非操作判断?false
         * and 测试：true
         * and 测试：false
         * or 测试：true
         * or 测试：true
         * isEqual 测试：true
         * isEqual 测试：false
         */
    }

    /**
     * 测试2：Functions函数, R apply(T t) ,一个入参，一个返回值
     */
    @Test
    public void test2() {
        // 1.简单测试
        Function<String, Integer> string2IntegerFunction = (source) -> {
            int anInt = 0;
            try {
                anInt = Integer.parseInt(source);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            return anInt;
        };
        Integer applyResult = string2IntegerFunction.apply("100");
        System.out.println(applyResult);

        Function<String, String> function1 = (source) -> "china-" + source + "-china";
        Function<String, String> function2 = (source) -> "ZH_CN-" + source + "-ZH_CN";

        //2.compose用法，先执行参数,default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
        //理解：A.compose(B),A组合调用B，那么函数会先执行B后再调用A本身
        Function<String, String> compose = function1.compose(function2);
        String result1 = compose.apply("4");
        System.out.println("composeResult=" + result1);//先执行e*e=16,再执行e*2等于32

        //3.andThen用法，先执行调用者,default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
        //理解：A.andThen(B),先执行为函数A后在执行B，顺序执行
        Function<String, String> andThen = function1.andThen(function1);
        String result2 = andThen.apply("4");
        System.out.println("andThenResult=" + result2);//先执行e*2等于8,再执行e*e等于64

        //4.identity用法，static <T> Function<T, T> identity(),传入什么返回什么
        Function<String, String> identity = Function.identity();
        String kiwipeach = identity.apply("kiwipeach");
        System.out.println(kiwipeach);

        Function<Person, Person> personIdentity = Function.identity();
        Person kiwipeachPerson = new Person("kiwipeach");
        Person identityPerson = personIdentity.apply(kiwipeachPerson);
        System.out.println(identityPerson == kiwipeachPerson);// true
    }

    /**
     * 测试3：Suppliers生产者，没有入参，返回给定类型的结果.  T get();
     */
    @Test
    public void test3() {
        Supplier<Person> personSupplier = Person::new;
        Person person1 = personSupplier.get();
        Person person2 = personSupplier.get();
        System.out.println(person1.hashCode()); //305623748
        System.out.println(person2.hashCode()); //758529971
        System.out.println(person1 == person2); //false
    }

    /**
     * 测试4：Consumers测试,接受一个入参. void accept(T t);
     */
    @Test
    public void test4() {
        Person me = new Person("kiwipeach");
        // void accept(T t) 测试
        Consumer<Person> personConsumer = (person) -> {
            System.out.println("个人消费:" + person.getName());
        };
        personConsumer.accept(me);
        // default Consumer<T> andThen(Consumer<? super T> after) 测试
        Consumer<Person> restaurantConsumer = (person) -> {
            System.out.println("餐厅消费:" + person.getName());
        };
        Consumer<Person> taxiConsumer = (person) -> {
            System.out.println("租车消费:" + person.getName());
        };
        Consumer<Person> meConsumer = restaurantConsumer.andThen(taxiConsumer);
        meConsumer.accept(me);
        /**
         * console result:
         * 个人消费:kiwipeach
         * 餐厅消费:kiwipeach
         * 租车消费:kiwipeach
         */
        /**
         * 拓展思路：可以使用java8中的生产者消费者模型完成Master-Slaver模式
         */
    }

    /**
     * 测试5：Comparators，接口中有很多默认的方法
     */
    @Test
    public void test5() {
        Person person1 = new Person("aaa", 18);
        Person person2 = new Person("bbb", 18);
        Person person3 = new Person("ccc", 30);
        Person person4 = new Person("ddd", 18);
        List<Person> personList = Arrays.asList(person1, person2, person3, person4);

        System.out.println("排序前:");
        //forEach方法在jdk1.8中开始出现，并且需要传入一个Consumer实现内部类
        personList.forEach((p) -> {
            System.out.print(p.getName() + "-" + p.getAge() + "\t");
        });
        //1.一般操作
        Comparator<Person> nameComparator = (p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName());
        //sort方法在jdk1.8中开始出现
        personList.sort(nameComparator);
        //2.高端操作,comparingInt,以下是不同姿势操作
        //Comparator<Person> ageComparator1 = Comparator.comparing((p) -> p.getAge());
        //Comparator<Person> ageComparator2 = Comparator.comparing(p -> p.getAge());
        //Comparator<Person> ageComparator3 = Comparator.comparingInt(p -> p.getAge());
        Comparator<Person> ageComparator4 = Comparator.comparingInt(Person::getAge);
        personList.sort(ageComparator4);
        System.out.println("\n按年龄排序后:");
        personList.forEach((p) -> {
            System.out.print(p.getName() + "-" + p.getAge() + "\t");
        });
        System.out.println("\n反向排序后:");
        personList.sort(ageComparator4.reversed());
        personList.forEach((p) -> {
            System.out.print(p.getName() + "-" + p.getAge() + "\t");
        });
    }

    /**
     * 测试6：Optionals不是一个函数接口,而是一个精巧的工具接口，用来防止NullPointerEception产生
     */
    @Test
    public void test6() throws RuntimeException {
        String nullStr = null;
        // 传统方式,判空不存在则赋值默认值
        // if (nullStr == null) nullStr = "kiwipeach";

        // 使用Optional,判空不存在则赋值默认值
        nullStr = Optional.ofNullable(nullStr).orElseGet(() -> "liuburu");

        System.out.println("nullStr = " + nullStr);

        //1.Optional有值,of
        Person person = new Person("kiwipeach", 25);
        Optional<Person> optional1 = Optional.of(person);
        System.out.println(optional1.isPresent());// true
        System.out.println(Optional.of(new Person()).isPresent());// true
        System.out.println(Optional.of("").isPresent());// true
        //System.out.println(Optional.of(null).isPresent());// 空指针异常

        //2.Optional为空,ofNullable
        System.out.println(Optional.ofNullable(null).isPresent());//false
        System.out.println(Optional.ofNullable("kiwpeach").isPresent());//true
        //2.获取内容,get
        if (optional1.isPresent()) {
            Person getPerson = optional1.get();
            System.out.println("获取内容:" + getPerson);
        }

        //3.Optional为空,ofNullable
        Optional<Person> optional2 = Optional.ofNullable(null);
        System.out.println(optional2.isPresent());//false

        //4.如果Optional实例有值则为其调用consumer，否则不做处理，ifPresent
        Optional.of(person).ifPresent(person1 -> {
            System.out.println("ifPresent开始消费:" + person);
        });

        //5.传入对象，如果有值则将其返回，否则返回指定的其它值，orElse
        System.out.println("orElse测试:" + Optional.ofNullable(null).orElse(new Person("错误xxx")));
        System.out.println("orElse测试:" + Optional.of(person).orElse(new Person("错误xxx")));
        //6.传入Supplier接口，如果有值则将其返回，否则返回指定的其它值，orElseGet
        Person resultPerson1 = optional1.orElseGet(() -> {
            return new Person("消费者");
        });
        Person resultPerson2 = Optional.ofNullable(person).orElseGet(() -> {
            return new Person("消费者");
        });
        System.out.println("orElseGet测试:" + resultPerson1);
        System.out.println("orElseGet测试:" + resultPerson2);
        //7.如果有值则将其返回，否则返回异常，orElseThrow
        try {
            optional2.orElseThrow(() -> {
                // 不要错写成了throw，应该是return，好坑。。。
                //throw new RuntimeException("person对象为空");
                return new RuntimeException("person对象为空");
            });

        } catch (RuntimeException e) {
            System.out.println("捕获到了异常:" + e.getLocalizedMessage());
        }

    }

    @Test
    public void testGetAndOrElseGet() {
        Person p = new Person();
        Person person01 = Optional.ofNullable(p).orElse(creawtePerson("person01"));// 理解：将函数返回值作为参数对象
        Person person02 = Optional.ofNullable(p).orElseGet(() -> creawtePerson("person02"));// 理解：将函数作为参数对象
        System.out.println("person01:" + person01.toString());
        System.out.println("person02:" + person02.toString());
        /**
         * p 对象不为空
         * person01 调用了 creawtePerson 函数
         * person01:Person{name='null', age=null}
         * person02:Person{name='null', age=null}
         *
         * p 对象为空
         * person01 调用了 creawtePerson 函数
         * person02 调用了 creawtePerson 函数
         * person01:Person{name='kiwipeach', age=39}
         * person02:Person{name='kiwipeach', age=39}
         *
         */
    }

    @Test
    public void 测试三木运算() {
        Person person = new Person();
        Person newPerson = person.getName() == null ? person : creawtePerson("三木");
        System.out.println("newPerson:" + newPerson.toString());
    }

    public Person creawtePerson(String invokerName) {
        Person person = new Person();
        person.setAge(39);
        person.setName("kiwipeach");
        System.out.println(invokerName + " 调用了 creawtePerson 函数");
        return person;
    }


    @Test
    public void nullAbleTest() {
        try {
            String inputStr = null;
            Optional.ofNullable(inputStr).orElseThrow(() -> new RuntimeException("xxxxxxxx"));
        } catch (RuntimeException runtimeException) {
            System.out.println("运行时异常信息:" + runtimeException.getLocalizedMessage());
        }
    }

    @Test
    public void nullAbleTest2() {
        Person p = null;
        Optional<Person> optional3 = Optional.ofNullable(p);
        optional3.orElseThrow(() -> new RuntimeException("yyyyyyyyyy"));
//        Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("yyyyyyyyyyyy"));
//        Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("xxxxxxxx"));

    }

    //@Test(expected = NoSuchElementException.class)
    @Test
    public void test7() {
        //抛出异常：java.util.NoSuchElementException: No value present
        Optional<Person> emptyOpt = Optional.empty();
        System.out.println("Optional.empty()==>" + emptyOpt.isPresent());

        //meInfo可能为空，当然也可能不能为空，模拟测试
        Person meInfo = getMeInfo();
        Optional<Person> optionalPerson = Optional.ofNullable(meInfo);
        if (optionalPerson.isPresent()) {
            System.out.println("person：" + optionalPerson.get());
        } else {
            System.out.println("随机数太小了，获取不到person对象信息");
        }

        //isPresent中传入Consumer对象
        Consumer<Person> ifPresentConsumer = p -> {
            System.out.println("开始消费：" + p);
        };
        optionalPerson.ifPresent(ifPresentConsumer);
    }

    private Person getMeInfo() {
        int random = (int) (Math.random() * 100);
        System.out.println("随机数：" + random);
        if (random > 50) {
            Person p = new Person();
            p.setAge(25);
            p.setName("kiwipeach");
            return p;
        }
        return null;
    }


    @Test
    public void test8() {
        Integer value1 = null;
        Integer value2 = new Integer(10);
        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> a = Optional.ofNullable(value1);
        //a.get();java.util.NoSuchElementException: No value present
        System.out.println(a.isPresent());//false
        // Optional.of - 如果传递的参数是 null，抛出异常 NullPointerException
        Optional<Integer> b = Optional.of(value2);


    }
}
